package com.rayson.yututu.manager;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
import com.rayson.yututu.config.CosProperties;
import com.rayson.yututu.exception.BusinessException;
import com.rayson.yututu.exception.ErrorCodeEnum;
import com.rayson.yututu.exception.ThrowUtils;
import com.rayson.yututu.model.dto.UploadPictureResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * Author: rayson
 * Description:
 * Version: 1.0
 * Date: 2024-12-24 22:39
 */
@Slf4j
@Service
public class FileManager {

    public static final long oneMB = 1024 * 1024;
    public static final Set<String> ALLOWED_FILE_TYPES = new HashSet<>(Arrays.asList("jpeg", "jpg", "png", "webp"));
    public static final Set<String> ALLOWED_FILE_TYPE_HEADERS = new HashSet<>(Arrays
            .asList("image/jpeg", "image/jpg", "image/png", "image/webp"));
    public String tempFilePath = "/tmp/";
    @Resource
    private CosManager cosManager;
    @Resource
    private CosProperties cosProperties;

    public UploadPictureResult uploadPicture(MultipartFile multipartFile, String pathPrefix, String pictureName) {
        // 校验图片
        validPic(multipartFile);
        // 图片上传地址
        String randomString = RandomUtil.randomString(16);
        String originalFilename = multipartFile.getOriginalFilename();
        String today = DateUtil.format(new Date(), "yyyyMMdd");
        String uploadFileName = String.format("%s_%s.%s", today, randomString, FileUtil.getSuffix(originalFilename));
        String uploadPath = String.format("/%s/%s", pathPrefix, uploadFileName);
        // 上传图片
        PutObjectResult putObjectResult = null;
        try {
            putObjectResult = cosManager.putPictureObject(uploadPath, multipartFile.getInputStream(), new ObjectMetadata());
        } catch (IOException e) {
            log.error("上传文件异常", e);
            throw new BusinessException(ErrorCodeEnum.SYSTEM_ERROR, "上传文件异常");
        }
        ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
        int width = imageInfo.getWidth();
        int height = imageInfo.getHeight();
        double picScale = NumberUtil.round(width * 1.0 / (double) height, 2).doubleValue();
        // 封装结果
        return UploadPictureResult.builder()
                .url(cosProperties.getHost() + uploadPath)
                .picName(StrUtil.isNotBlank(pictureName) ? pictureName : FileUtil.mainName(originalFilename))
                .picSize(multipartFile.getSize())
                .picWidth(width)
                .picHeight(height)
                .picScale(picScale)
                .picFormat(imageInfo.getFormat())
                .build();
    }

    public UploadPictureResult uploadPictureByUrl(String fileUrl, String pathPrefix, String pictureName) {
        validPic(fileUrl);
        // 图片上传地址
        String randomString = RandomUtil.randomString(16);
        String originalFilename = FileUtil.mainName(fileUrl);
        String today = DateUtil.format(new Date(), "yyyyMMdd");
        String uploadFileName = String.format("%s_%s.%s", today, randomString, FileUtil.getSuffix(originalFilename));
        String uploadPath = String.format("/%s/%s", pathPrefix, uploadFileName);
        // 上传图片
        PutObjectResult putObjectResult = null;
        File destFile = null;
        Long picSize = null;
        try {
            // 下载文件到本地
            destFile = new File(tempFilePath + originalFilename);
            HttpUtil.downloadFile(fileUrl, destFile);
            picSize = FileUtil.size(destFile);
            putObjectResult = cosManager.putPictureObject(uploadPath, IoUtil.toStream(destFile), new ObjectMetadata());
        } catch (Exception e) {
            log.error("上传文件异常", e);
            throw new BusinessException(ErrorCodeEnum.SYSTEM_ERROR, "上传文件异常");
        } finally {
            if (destFile != null) {
                destFile.delete();
            }
        }
        ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
        int width = imageInfo.getWidth();
        int height = imageInfo.getHeight();
        double picScale = NumberUtil.round(width * 1.0 / (double) height, 2).doubleValue();
        // 封装结果
        return UploadPictureResult.builder()
                .url(cosProperties.getHost() + uploadPath)
                .picName(StrUtil.isNotBlank(pictureName) ? pictureName : FileUtil.mainName(originalFilename))
                .picSize(picSize)
                .picWidth(width)
                .picHeight(height)
                .picScale(picScale)
                .picFormat(imageInfo.getFormat())
                .build();
    }

    private void validPic(String fileUrl) {
        // 1.url非空
        ThrowUtils.throwIf(StrUtil.isBlank(fileUrl), ErrorCodeEnum.PARAM_ERROR, "图片url不能为空");
        // 2.url格式
        try {
            new URL(fileUrl);
        } catch (MalformedURLException e) {
            throw new BusinessException(ErrorCodeEnum.PARAM_ERROR, "图片url格式不正确");
        }
        // 3.url协议
        ThrowUtils.throwIf(!fileUrl.startsWith("http://") && !fileUrl.startsWith("https://"),
                ErrorCodeEnum.PARAM_ERROR, "仅支持 HTTP 和 HTTPS 协议");
        // 4.发送HEAD请求验证文件是否存在？
        HttpResponse httpResponse = null;
        try {
            httpResponse = HttpUtil.createRequest(Method.HEAD, fileUrl).execute();
            if (httpResponse.getStatus() != HttpStatus.HTTP_OK) {
                // 有些网站不支持这种方式来校验文件是否存在
                return;
            }
            // 5.校验文件类型
            String contentType = httpResponse.header("Content-Type");
            if (StrUtil.isNotBlank(contentType)) {
                ThrowUtils.throwIf(!ALLOWED_FILE_TYPE_HEADERS.contains(contentType), ErrorCodeEnum.PARAM_ERROR, "图片格式不支持");
            }
            // 6.校验图片大小
            String contentLength = httpResponse.header("Content-Length");
            if (StrUtil.isNotBlank(contentLength)) {
                long fileSize = Long.parseLong(contentLength);
                ThrowUtils.throwIf(fileSize > 2 * oneMB, ErrorCodeEnum.PARAM_ERROR, "图片大小不能超过2MB");
            }
        } finally {
            IOUtils.closeQuietly(httpResponse);
        }
    }

    private void validPic(MultipartFile multipartFile) {
        ThrowUtils.throwIf(multipartFile == null, ErrorCodeEnum.PARAM_ERROR, "图片不能为空");
        // 校验文件大小
        long fileSize = multipartFile.getSize();
        ThrowUtils.throwIf(fileSize > 2 * oneMB, ErrorCodeEnum.PARAM_ERROR, "图片大小不能超过2MB");
        String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename());
        ThrowUtils.throwIf(!ALLOWED_FILE_TYPES.contains(fileSuffix), ErrorCodeEnum.PARAM_ERROR, "图片格式不支持");
    }
}
